import wx
import ldap3


class LdapLogin(wx.Frame):
    def __init__(self, callback, ldapServer, ldapPort, logger, headerInfo):
        super(LdapLogin, self).__init__(None, title="", style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP)
        self._logger = logger
        self._ldapServer = ldapServer
        self._ldapPort = ldapPort
        self._login_success = False
        self._login_dialog = None
        self._headerLogInfo = headerInfo
        self.callback = callback
        self.InitGUI()

    def InitGUI(self):
        self._logger.debug(self._headerLogInfo + "Initializing login window")
        self.Bind(wx.EVT_CLOSE, self.onClose)
        user_sizer = wx.BoxSizer(wx.HORIZONTAL)

        user_lbl = wx.StaticText(self, label="Username:")
        user_sizer.Add(user_lbl, 0, wx.ALL | wx.CENTER, 5)
        self._username = wx.TextCtrl(self, value="")
        self._username.Bind(wx.EVT_KEY_DOWN, self.onKeyDownUser)
        user_sizer.Add(self._username, 0, wx.ALL, 5)

        p_sizer = wx.BoxSizer(wx.HORIZONTAL)

        p_lbl = wx.StaticText(self, label="Password:")
        p_sizer.Add(p_lbl, 0, wx.ALL | wx.CENTER, 5)
        self._password = wx.TextCtrl(self, value="", style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER)
        self._password.Bind(wx.EVT_TEXT_ENTER, self.onLogin)
        self._password.Bind(wx.EVT_KEY_DOWN, self.onKeyDownPass)
        p_sizer.Add(self._password, 0, wx.ALL, 5)

        self.SetSize((210, 170))

        b_sizer = wx.BoxSizer(wx.HORIZONTAL)
        btn_login = wx.Button(self, label="Proceed")
        btn_login.Bind(wx.EVT_BUTTON, self.onLogin)
        b_sizer.Add(btn_login, 0, wx.ALL | wx.LEFT, 5)

        btn_cancel = wx.Button(self, label="Cancel")
        btn_cancel.Bind(wx.EVT_BUTTON, self.onClose)
        b_sizer.Add(btn_cancel, 0, wx.ALL, 5)

        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(user_sizer, 0, wx.ALL, 5)
        main_sizer.Add(p_sizer, 0, wx.ALL, 5)
        main_sizer.Add(b_sizer, 0, wx.ALL, 5)

        self.SetSizer(main_sizer)
        self._logger.debug(self._headerLogInfo + "Login window initialized")
        self.Show()

    def onKeyDownUser(self, event):
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_TAB:
            self._password.Enable(True)
            self._password.SetFocus()
            self._password.SetInsertionPointEnd()
            event.EventObject.Navigate()
        event.Skip()

    def onKeyDownPass(self, event):
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_TAB:
            self._username.Enable(True)
            self._username.SetFocus()
            self._username.SetInsertionPointEnd()
            event.EventObject.Navigate()
        event.Skip()

    def onLogin(self, event):
        username = str(self._username.GetValue())
        password = str(self._password.GetValue())
        self._logger.debug(self._headerLogInfo + "Connecting to ldap, user: {}".format(username))
        base_dn = "ou=people,o=nsn"
        server = ldap3.Server(self._ldapServer, port=self._ldapPort, get_info=ldap3.ALL)
        search_filter = '(uid=' + username + ')'
        try:
            connection = ldap3.Connection(server, user=username, password=password)
            connection.bind()
            connection.search(base_dn, search_filter)
            if not connection.response:
                self._logger.debug(self._headerLogInfo + "User: {} not found in ldap".format(username))
                self.callback(False, username, "No such user")
            else:
                user_connection = ldap3.Connection(server, user=connection.response[0]['dn'], password=password)
                user_connection.bind()
                if user_connection.bound:
                    user_connection.unbind()
                    self.callback(True, username, "")
                else:
                    self._logger.debug(self._headerLogInfo + "Authentication failed, user: {}".format(username))
                    self.callback(False, username, "Authentication failed")
        except Exception as e:
            self._logger.exception(self._headerLogInfo + "Exception while connecting to ldap. User: {}. Exception: {}"
                                   .format(username, str(e)))
            connection.unbind()
            self.callback(False, username, "Exception: {}".format(str(e)))

    def onClose(self, event):
        self.callback(False, "unknown", "Login operation cancelled")
